/*
 * Copyright 2025 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.beit.mybatis.demo.common.mapper.entity;

import tk.mybatis.mapper.MapperException;
import tk.mybatis.mapper.entity.EntityColumn;
import tk.mybatis.mapper.entity.EntityTable;
import tk.mybatis.mapper.entity.Example;
import tk.mybatis.mapper.entity.SqlsCriteria;
import tk.mybatis.mapper.mapperhelper.EntityHelper;
import tk.mybatis.mapper.util.Sqls;
import tk.mybatis.mapper.util.StringUtil;

import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class CustomCondition extends Example {
    public CustomCondition(Class<?> entityClass) {
        super(entityClass);
    }

    public CustomCondition(Class<?> entityClass, boolean exists) {
        super(entityClass, exists);
    }

    public CustomCondition(Class<?> entityClass, boolean exists, boolean notNull) {
        super(entityClass, exists, notNull);
    }

    public static Builder newBuilder(Class<?> entityClass) {
        return new Builder(entityClass);
    }

    private CustomCondition(Builder builder) {
        super(builder.entityClass, builder.exists, builder.notNull);
        super.distinct = builder.distinct;
        super.propertyMap = builder.propertyMap;
        super.selectColumns = builder.selectColumns;
        super.excludeColumns = builder.excludeColumns;
        super.oredCriteria = builder.exampleCriterias;
        super.forUpdate = builder.forUpdate;
        super.tableName = builder.tableName;
        super.ORDERBY = new OrderBy(this, propertyMap);

        if (!StringUtil.isEmpty(builder.orderByClause.toString())) {
            super.orderByClause = builder.orderByClause.toString();
        }
    }

    public static class Criteria extends Example.Criteria {

        protected Criteria(Map<String, EntityColumn> propertyMap, boolean exists, boolean notNull) {
            super(propertyMap, exists, notNull);
        }

        @Override
        protected void addCriterion(String condition) {
            super.addCriterion(condition);
        }

        @Override
        protected void addCriterion(String condition, Object value, String property) {
            super.addCriterion(condition, value, property);
        }

        // 重写方法1
        @Override
        protected void addCriterion(String condition, Object value1, Object value2, String property) {
            super.criteria.add(new CustomCriterion(condition, value1, value2));
        }

        @Override
        protected void addOrCriterion(String condition) {
            super.addOrCriterion(condition);
        }

        @Override
        protected void addOrCriterion(String condition, Object value, String property) {
            super.addOrCriterion(condition, value, property);
        }

        // 重写方法2
        @Override
        protected void addOrCriterion(String condition, Object value1, Object value2, String property) {
            super.criteria.add(new CustomCriterion(condition, value1, value2, true));
        }
    }

    public static class Builder {

        private final Class<?> entityClass;
        protected EntityTable table;
        //属性和列对应
        protected Map<String, EntityColumn> propertyMap;
        private StringBuilder orderByClause;
        private boolean distinct;
        private final boolean exists;
        private final boolean notNull;
        private boolean forUpdate;
        //查询字段
        private Set<String> selectColumns;
        //排除的查询字段
        private Set<String> excludeColumns;
        private String countColumn;
        private final List<Sqls.Criteria> sqlsCriteria;
        //动态表名
        private List<Example.Criteria> exampleCriterias;
        //动态表名
        private String tableName;

        public Builder(Class<?> entityClass) {
            this(entityClass, true);
        }

        public Builder(Class<?> entityClass, boolean exists) {
            this(entityClass, exists, false);
        }

        public Builder(Class<?> entityClass, boolean exists, boolean notNull) {
            this.entityClass = entityClass;
            this.exists = exists;
            this.notNull = notNull;
            this.orderByClause = new StringBuilder();
            this.table = EntityHelper.getEntityTable(entityClass);
            this.propertyMap = table.getPropertyMap();
            this.sqlsCriteria = new ArrayList<>(2);
        }

        public Builder distinct() {
            return setDistinct(true);
        }

        public Builder forUpdate() {
            return setForUpdate(true);
        }

        public Builder selectDistinct(String... properties) {
            select(properties);
            this.distinct = true;
            return this;
        }

        public Builder select(String... properties) {
            if (properties != null && properties.length > 0) {
                if (this.selectColumns == null) {
                    this.selectColumns = new LinkedHashSet<String>();
                }
                for (String property : properties) {
                    if (this.propertyMap.containsKey(property)) {
                        this.selectColumns.add(propertyMap.get(property).getColumn());
                    } else {
                        throw new MapperException("当前实体类不包含名为" + property + "的属性!");
                    }
                }
            }
            return this;
        }

        public Builder notSelect(String... properties) {
            if (properties != null && properties.length > 0) {
                if (this.excludeColumns == null) {
                    this.excludeColumns = new LinkedHashSet<String>();
                }
                for (String property : properties) {
                    if (propertyMap.containsKey(property)) {
                        this.excludeColumns.add(propertyMap.get(property).getColumn());
                    } else {
                        throw new MapperException("当前实体类不包含名为" + property + "的属性!");
                    }
                }
            }
            return this;
        }

        public Builder from(String tableName) {
            return setTableName(tableName);
        }

        public Builder where(Sqls sqls) {
            Sqls.Criteria criteria = sqls.getCriteria();
            criteria.setAndOr("and");
            this.sqlsCriteria.add(criteria);
            return this;
        }

        public Builder where(SqlsCriteria sqls) {
            Sqls.Criteria criteria = sqls.getCriteria();
            criteria.setAndOr("and");
            this.sqlsCriteria.add(criteria);
            return this;
        }

        public Builder andWhere(Sqls sqls) {
            Sqls.Criteria criteria = sqls.getCriteria();
            criteria.setAndOr("and");
            this.sqlsCriteria.add(criteria);
            return this;
        }

        public Builder andWhere(SqlsCriteria sqls) {
            Sqls.Criteria criteria = sqls.getCriteria();
            criteria.setAndOr("and");
            this.sqlsCriteria.add(criteria);
            return this;
        }

        public Builder orWhere(Sqls sqls) {
            Sqls.Criteria criteria = sqls.getCriteria();
            criteria.setAndOr("or");
            this.sqlsCriteria.add(criteria);
            return this;
        }

        public Builder orWhere(SqlsCriteria sqls) {
            Sqls.Criteria criteria = sqls.getCriteria();
            criteria.setAndOr("or");
            this.sqlsCriteria.add(criteria);
            return this;
        }

        public Builder orderBy(String... properties) {
            return orderByAsc(properties);
        }

        public Builder orderByAsc(String... properties) {
            contactOrderByClause(" Asc", properties);
            return this;
        }

        public Builder orderByDesc(String... properties) {
            contactOrderByClause(" Desc", properties);
            return this;
        }

        private void contactOrderByClause(String order, String... properties) {
            StringBuilder columns = new StringBuilder();
            for (String property : properties) {
                String column;
                if ((column = propertyForOderBy(property)) != null) {
                    columns.append(",").append(column).append(order);
                }
            }
            ;
            if (!columns.isEmpty()) {
                orderByClause.append(columns);
            }
        }

        public CustomCondition build() {
            this.exampleCriterias = new ArrayList<>();
            for (Sqls.Criteria criteria : sqlsCriteria) {
                Criteria customCriteria = new Criteria(this.propertyMap, this.exists, this.notNull);
                customCriteria.setAndOr(criteria.getAndOr());
                for (Sqls.Criterion criterion : criteria.getCriterions()) {
                    String condition = criterion.getCondition();
                    String andOr = criterion.getAndOr();
                    String property = criterion.getProperty();
                    Object[] values = criterion.getValues();
                    transformCriterion(customCriteria, condition, property, values, andOr);
                }
                exampleCriterias.add(customCriteria);
            }

            if (!this.orderByClause.isEmpty()) {
                this.orderByClause = new StringBuilder(this.orderByClause.substring(1, this.orderByClause.length()));
            }

            return new CustomCondition(this);
        }

        private void transformCriterion(Criteria exampleCriteria, String condition, String property, Object[] values, String andOr) {
            if (values.length == 0) {
                if ("and".equals(andOr)) {
                    exampleCriteria.addCriterion(columnNew(property) + " " + condition);
                } else {
                    exampleCriteria.addOrCriterion(columnNew(property) + " " + condition);
                }
            } else if (values.length == 1) {
                if ("and".equals(andOr)) {
                    exampleCriteria.addCriterion(columnNew(property) + " " + condition, values[0], propertyNew(property));
                } else {
                    exampleCriteria.addOrCriterion(columnNew(property) + " " + condition, values[0], propertyNew(property));
                }
            } else if (values.length == 2) {
                if ("and".equals(andOr)) {
                    exampleCriteria.addCriterion(columnNew(property) + " " + condition, values[0], values[1], propertyNew(property));
                } else {
                    exampleCriteria.addOrCriterion(columnNew(property) + " " + condition, values[0], values[1], propertyNew(property));
                }
            }
        }

        private String columnNew(String property) {
            if (property.contains(",")) {
                return Stream.of(property.split(",")).map(this::column)
                        .collect(Collectors.joining(",", "(", ")"));
            } else {
                return column(property);
            }
        }

        private String propertyNew(String property) {
            if (property.contains(",")) {
                return property;
            } else {
                return property(property);
            }
        }

        private String column(String property) {
            if (propertyMap.containsKey(property)) {
                return propertyMap.get(property).getColumn();
            } else if (exists) {
                throw new MapperException("当前实体类不包含名为" + property + "的属性!");
            } else {
                return null;
            }
        }

        private String property(String property) {
            if (propertyMap.containsKey(property)) {
                return property;
            } else if (exists) {
                throw new MapperException("当前实体类不包含名为" + property + "的属性!");
            } else {
                return null;
            }
        }

        private String propertyForOderBy(String property) {
            if (StringUtil.isEmpty(property) || StringUtil.isEmpty(property.trim())) {
                throw new MapperException("接收的property为空！");
            }
            property = property.trim();
            if (!propertyMap.containsKey(property)) {
                throw new MapperException("当前实体类不包含名为" + property + "的属性!");
            }
            return propertyMap.get(property).getColumn();
        }

        public Builder setDistinct(boolean distinct) {
            this.distinct = distinct;
            return this;
        }

        public Builder setForUpdate(boolean forUpdate) {
            this.forUpdate = forUpdate;
            return this;
        }

        public Builder setTableName(String tableName) {
            this.tableName = tableName;
            return this;
        }
    }
}
